package com.ceair.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ceair.entity.Oauth2BasicUser;
import com.ceair.entity.SysRole;
import com.ceair.entity.SysUserRole;
import com.ceair.entity.request.user.*;
import com.ceair.entity.vo.user.Oauth2BasicUserRoleVO;
import com.ceair.entity.vo.user.Oauth2BasicUserVO;
import com.ceair.entity.vo.role.SysRoleVO;
import com.ceair.enums.DeleteStatus;
import com.ceair.enums.ResultCode;
import com.ceair.exception.BusinessException;
import com.ceair.mapper.Oauth2BasicUserSystemMapper;
import com.ceair.service.IOauth2BasicUserService;
import com.ceair.service.ISysRoleService;
import com.ceair.service.ISysUserRoleService;
import com.ceair.utils.structMapper.Oauth2BasicUserStructMapper;
import com.ceair.utils.structMapper.SysRoleStructMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author BOB
 * @description 针对表【oauth2_basic_user(基础用户信息表)】的数据库操作Service实现
 * @createDate 2025-02-14 23:20:45
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class Oauth2BasicUserServiceImpl extends ServiceImpl<Oauth2BasicUserSystemMapper, Oauth2BasicUser>
        implements IOauth2BasicUserService {

    private final ISysRoleService sysRoleService;
    private final ISysUserRoleService sysUserRoleService;

    /**
     * 分页查询Oauth2基础用户信息
     * <p>
     * 该方法根据传入的请求参数，初始化分页工具并调用Mapper进行分页查询，返回分页后的用户信息。
     *
     * @param oauth2BasicUserReq 包含分页参数和查询条件的请求对象
     * @return Page<Oauth2BasicUser> 分页查询结果，包含用户信息列表及分页信息
     */
    @Override
    public Page<Oauth2BasicUser> queryUserWithPage(Oauth2BasicUserReq oauth2BasicUserReq) {
        // 初始化分页工具以及参数
        Page<Oauth2BasicUser> page = new Page<>(oauth2BasicUserReq.getPageReq().getCurrent(),
                oauth2BasicUserReq.getPageReq().getSize());

        // 调用mapper分页查询
        return baseMapper.queryUserWithPage(page, oauth2BasicUserReq);
    }

    /**
     * 保存或更新Oauth2基础用户信息
     * <p>
     * 此方法首先会将输入的用户信息请求对象中的非空属性复制到一个新的Oauth2BasicUser对象中，
     * 然后检查用户的密码是否为空如果密码不为空，则使用BCryptPasswordEncoder进行加密，
     * 以确保密码的安全性如果密码为空，则将密码设置为null，以避免存储无效的密码信息
     * 最后，调用saveOrUpdate方法来保存或更新用户信息，并通过映射转换为VO对象返回
     *
     * @param oauth2BasicUserInOrUpReq 包含用户信息的请求对象，用于创建或更新用户
     * @return 返回一个Oauth2BasicUserVO对象，表示保存后的用户信息
     */
    @Override
    public Oauth2BasicUserVO save(Oauth2BasicUserInOrUpReq oauth2BasicUserInOrUpReq) {
        // 初始化
        Oauth2BasicUser oauth2BasicUser = new Oauth2BasicUser();

        // 将 oauth2BasicUserInOrUpReq 中不是空的元素复制到 oauth2BasicUser 中
        BeanUtil.copyProperties(oauth2BasicUserInOrUpReq, oauth2BasicUser, CopyOptions.create().ignoreNullValue());

        // 对密码元素使用 BCryptPasswordEncoder 进行加密
        if (!StringUtils.isBlank(oauth2BasicUser.getPassword())) {
            oauth2BasicUser.setPassword(new BCryptPasswordEncoder().encode(oauth2BasicUser.getPassword()));
        } else {
            oauth2BasicUser.setPassword(null);
        }

        // 直接使用 saveOrUpdate 方法，如果存在则更新，不存在则插入
        saveOrUpdate(oauth2BasicUser);

        // 将保存后的用户对象转换为VO对象返回
        return Oauth2BasicUserStructMapper.INSTANCE.toVO(oauth2BasicUser);
    }

    /**
     * 获取用户角色列表
     *
     * @param oauth2BasicUserRoleReq 用户角色请求对象，包含用户ID
     * @return 返回用户角色信息对象，包括用户已分配的角色和所有角色列表
     */
    @Override
    public Oauth2BasicUserRoleVO getUserRoleList(Oauth2BasicUserRoleReq oauth2BasicUserRoleReq) {
        return this.getUserRoleList(oauth2BasicUserRoleReq.getId());
    }

    /**
     * 更新用户的角色信息
     * 此方法首先删除用户现有的所有角色关联，然后根据传入的角色ID列表重新建立关联
     * 如果传入的角色ID列表为空，则用户将没有任何角色关联
     *
     * @param updateUserRolesReq 包含用户ID和角色ID列表的请求对象
     * @return 返回更新后的用户角色信息
     */
    @Override
    @Transactional
    public Oauth2BasicUserRoleVO updateUserRoles(UpdateUserRolesReq updateUserRolesReq) {
        try {
            // 根据用户id删除用户-角色关联信息
            sysUserRoleService.lambdaUpdate()
                    .eq(SysUserRole::getUserId, updateUserRolesReq.getId())
                    .remove();

            // 如果角色ID清单为空，直接返回用户角色列表
            if (updateUserRolesReq.getRoleIds().isEmpty()) {
                return this.getUserRoleList(updateUserRolesReq.getId());
            }

            // 根据 updateUserRolesReq 中的 角色id清单，查询现有角色列表
            List<SysRole> sysRoles = sysRoleService.lambdaQuery()
                    .in(SysRole::getId, updateUserRolesReq.getRoleIds())
                    .list();

            // 如果 sysRoles 是空的代表前端传入的角色列表已经是旧的不顶用了，同样不需要更新关联关系直接结束，否则要更新关系
            if (!sysRoles.isEmpty()) {
                // 构建批量插入的数据
                List<SysUserRole> userRolesToSave = sysRoles.stream()
                        .map(sysRole -> {
                            SysUserRole sysUserRole = new SysUserRole();
                            sysUserRole.setUserId(updateUserRolesReq.getId());
                            sysUserRole.setRoleId(sysRole.getId());
                            return sysUserRole;
                        })
                        .collect(Collectors.toList());

                // 批量插入用户-角色关联信息
                sysUserRoleService.saveBatch(userRolesToSave);
            }
        } catch (Exception e) {
            log.error("更新用户角色失败", e);
            throw new BusinessException(ResultCode.FAILED.getCode(), e.getMessage());
        }

        return this.getUserRoleList(updateUserRolesReq.getId());
    }

    /**
     * 删除用户方法
     *
     * 该方法接收一个DeleteUserReq对象，其中包含要删除的用户ID列表方法首先检查这些用户是否有关联的角色，
     * 如果有关联角色，则删除操作失败如果用户没有关联角色，则将这些用户的删除状态标记为已删除
     *
     * @param deleteUserReq 包含要删除的用户ID列表的请求对象
     * @return 如果用户成功删除或标记为已删除，则返回true；否则返回false
     */
    @Override
    @Transactional
    public Boolean delete(DeleteUserReq deleteUserReq) {
        // 获取 用户ID
        List<Long> ids = deleteUserReq.getIds();

        // 根据 用户 ids 查询 用户角色绑定关系
        List<SysUserRole> sysUserRoles = sysUserRoleService.lambdaQuery()
                .in(SysUserRole::getUserId, ids)
                .list();

        // 检查是否有用户角色绑定关系存在，如果有，则删除操作失败
        if (!sysUserRoles.isEmpty()) {
            log.info("有用户-角色绑定关系未清理，删除用户失败");
            throw new BusinessException(ResultCode.FAILED.getCode(), "有用户-角色绑定关系未清理，删除用户失败");
        }

        // 构建批量逻辑删除用户对象
        List<Oauth2BasicUser> oauth2BasicUsers = ids.stream().map(id -> {
            Oauth2BasicUser oauth2BasicUser = new Oauth2BasicUser();
            oauth2BasicUser.setId(id);
            oauth2BasicUser.setDeleted(DeleteStatus.DELETED.getCode());
            return oauth2BasicUser;
        }).toList();

        // 执行批量逻辑删除用户
        return updateBatchById(oauth2BasicUsers);
    }

    /**
     * 获取用户角色列表
     *
     * @param id 用户ID
     * @return 包含用户角色信息的对象
     */
    private Oauth2BasicUserRoleVO getUserRoleList(Long id) {
        // 根据用户ID查询用户-角色关联信息
        List<SysUserRole> sysUserRoles = sysUserRoleService.lambdaQuery()
                .eq(SysUserRole::getUserId, id)
                .list();

        // 获取所有角色列表，并转换为SysRoleVO对象
        List<SysRoleVO> allRolesList = getAllRolesAsVOs(id);

        // 根据用户-角色关联信息获取已分配的角色列表，并转换为SysRoleVO对象
        List<SysRoleVO> assignRoles = getAssignedRolesAsVOs(sysUserRoles, id);

        // 构建并返回用户角色信息对象
        return Oauth2BasicUserRoleVO.builder()
                .id(id)
                .assignRoles(assignRoles)
                .allRolesList(allRolesList)
                .build();
    }

    /**
     * 获取所有角色信息并转换为SysRoleVO对象列表
     * 此方法用于从系统中获取所有角色信息，并将每个角色信息转换为SysRoleVO对象
     * 如果获取角色列表时发生异常，将记录错误日志并返回空列表
     *
     * @param id 包含用户ID的请求对象，用于记录错误日志
     * @return 角色信息的SysRoleVO对象列表，如果获取失败或没有角色，则返回空列表
     */
    private List<SysRoleVO> getAllRolesAsVOs(Long id) {
        try {
            // 尝试从数据库中获取所有角色信息，如果为空则返回空列表
            return Optional.ofNullable(sysRoleService.lambdaQuery().list())
                    .orElse(Collections.emptyList())
                    .stream()
                    // 将每个角色实体对象转换为SysRoleVO对象
                    .map(this::convertToVO)
                    // 过滤掉任何可能转换失败（为null）的SysRoleVO对象
                    .filter(Objects::nonNull)
                    .toList();
        } catch (Exception e) {
            // 记录获取角色信息时发生的异常信息
            log.error("获取所有角色失败，用户ID: {}, 失败原因: {}", id, e.getMessage(), e);
            // 返回空列表，表示获取角色信息失败
            return Collections.emptyList();
        }
    }

    /**
     * 根据用户角色关联信息获取已分配的角色信息
     *
     * @param sysUserRoles 用户角色关联列表，用于获取用户所关联的角色ID
     * @param id 用户角色请求对象，包含用户ID等信息，用于日志记录
     * @return 返回转换为SysRoleVO对象的已分配角色列表如果输入为空或null，或者查询失败，将返回空列表
     */
    private List<SysRoleVO> getAssignedRolesAsVOs(List<SysUserRole> sysUserRoles, Long id) {
        // 检查输入列表是否为空或null，如果是，则直接返回空列表
        if (sysUserRoles == null || sysUserRoles.isEmpty()) {
            return Collections.emptyList();
        }

        try {
            // 从用户角色关联列表中提取角色ID列表
            List<Long> roleIds = sysUserRoles.stream()
                    .map(SysUserRole::getRoleId)
                    .collect(Collectors.toList());

            // 根据角色ID列表查询角色信息，并将结果转换为SysRoleVO对象列表
            return Optional.ofNullable(sysRoleService.lambdaQuery().in(SysRole::getId, roleIds).list())
                    .orElse(Collections.emptyList())
                    .stream()
                    .map(this::convertToVO)
                    .filter(Objects::nonNull)
                    .toList();
        } catch (Exception e) {
            // 如果查询过程中发生异常，记录错误日志，并返回空列表
            log.error("根据用户查询角色失败，用户ID: {}, 失败原因: {}", id, e.getMessage(), e);
            return Collections.emptyList();
        }
    }

    /**
     * 将SysRole实体对象转换为SysRoleVO视图对象
     * 此方法使用SysRoleStructMapper进行转换，旨在将数据库实体转换为传输或展示层对象
     *
     * @param sysRole SysRole实体对象，包含角色基本信息
     * @return SysRoleVO视图对象，如果转换过程中发生异常，则返回null
     */
    private SysRoleVO convertToVO(SysRole sysRole) {
        try {
            // 使用SysRoleStructMapper进行实体到视图对象的转换
            return SysRoleStructMapper.INSTANCE.toVO(sysRole);
        } catch (Exception e) {
            // 捕获转换过程中的异常，并记录错误日志
            log.error("转换角色信息失败，角色ID: {}, 具体原因为: {}", sysRole.getId(), e.getMessage());
            // 异常情况下，返回null
            return null;
        }
    }
}




